package com.petshop.service.impl;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.petshop.controller.UploadController;
import com.petshop.dto.PetOrder;
import com.petshop.mapper.OrdersMapper;
import com.petshop.mapper.PetProductMapper;
import com.petshop.mapper.UsersMapper;
import com.petshop.pojo.Orders;
import com.petshop.pojo.PetProduct;
import com.petshop.pojo.Users;
import com.petshop.service.PetProductService;
import com.petshop.utils.JwtHelper;
import com.petshop.utils.Result;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import static com.petshop.utils.RedisConstant.*;

/**
 * @author 20289
 * @description 针对表【pet_product】的数据库操作Service实现
 * @createDate 2025-03-04 20:57:21
 */
@Service
public class PetProductServiceImpl extends ServiceImpl<PetProductMapper, PetProduct>
        implements PetProductService {

    @Autowired
    private PetProductMapper petProductMapper;

    @Autowired
    private JwtHelper jwtHelper;

    @Autowired
    private UsersMapper usersMapper;

    @Autowired
    private OrdersMapper ordersMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private UploadController uploadController;

    /**
     * 增加一个宠物商品信息
     *
     * @param petProduct
     */
    @Override
    public Result insertPetProduct(@NotNull PetProduct petProduct) {
        LambdaQueryWrapper<PetProduct> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //不允许商品重名
        lambdaQueryWrapper.eq(PetProduct::getGoodName, petProduct.getGoodName());
        Long count = petProductMapper.selectCount(lambdaQueryWrapper);
        //库存中有该商品，显示商品已存在，请修改商品库存
        if (count > 0) {
            return Result.error("商品已经存在！请修改库存");
        }
        //库存中没有该商品，添加一条新的商品记录
        petProductMapper.insert(petProduct);
        return Result.ok(petProduct);
    }

    /**
     * 下架宠物商品
     *
     * @param goodId
     */
    @Override
    public Result outPetProduct(Integer goodId) {
        PetProduct petProduct = getById(goodId);
        //库存中没有该商品
        if (petProduct == null) {
            return Result.error("商品已经被删除！");
        }
        //存在商品,直接下架
        removeById(goodId);
        return Result.ok("删除成功");
    }

    /**
     * 批量删除宠物商品
     *
     * @param idList
     * @return
     */
    @Override
    public Result outBatchPetProduct(ArrayList<Integer> idList) {
        int i = petProductMapper.deleteBatchIds(idList);
        if (i > 0) {
            return Result.ok("已经删除" + i + "个商品！");
        }
        return Result.error("批量删除失败！");
    }

    /**
     * 购买一只宠物
     *
     * @param petOrder
     * @param token
     * @return
     */
    @Override
    public Result buyPetProduct(@NotNull PetOrder petOrder, String token) {
        int userId = jwtHelper.getUserId(token).intValue();
        LambdaQueryWrapper<Users> queryWrapper = new LambdaQueryWrapper<>();
        Users user = usersMapper.selectOne(queryWrapper.eq(Users::getUserId, userId));
        //比较余额和总价
        LambdaQueryWrapper<PetProduct> petProductLambdaQueryWrapper = new LambdaQueryWrapper<>();
        petProductLambdaQueryWrapper.eq(PetProduct::getGoodId, petOrder.getPetId());
        PetProduct petProduct = petProductMapper.selectOne(petProductLambdaQueryWrapper);
        BigDecimal totalPrice = petProduct.getGoodPrice().multiply(BigDecimal.valueOf(petOrder.getCount()));
        if (user.getAccount().subtract(totalPrice).compareTo(BigDecimal.ZERO) < 0) {
            return Result.error("余额不足！请及时充值");
        }
        //加入商品信息到订单里
        Orders order = new Orders();
        order.setUserId(userId);
        order.setName(petProduct.getGoodName());
        order.setPrice(petProduct.getGoodPrice());
        order.setProductId(null);
        order.setBuyNumber(petOrder.getCount());
        order.setGoodId(petOrder.getPetId());
        order.setTotalAmount(totalPrice);
        order.setOrderImage(petProduct.getPetImage());
        order.setStatus("待发货");
        //加入地址信息
        order.setAddress(petOrder.getAddress().getAddress());
        order.setReceiver(petOrder.getAddress().getReceiver());
        order.setPhoneNumber(petOrder.getAddress().getPhone());
        //加入创建修改时间信息
        order.setCreatedAt(new Date());
        order.setUpdatedAt(new Date());
        //添加到数据库里
        int i = ordersMapper.insert(order);
        if (i > 0) {
            //修改库存
            petProductMapper.updateReduceStock(petOrder.getPetId(), petOrder.getCount());
        }
        return Result.ok("下单成功");
    }

    /**
     * 修改宠物商品信息
     * 采用主动更新的更新策略
     *
     * @param petProduct
     */
    @Override
    public Result updatePetProduct(@org.jetbrains.annotations.NotNull PetProduct petProduct) {
        Integer id = petProduct.getGoodId();
        if (id == null) {
            return Result.error("商品id不能为空！");
        }
        //1.更新数据库
        Integer version = petProductMapper.selectById(petProduct.getGoodId()).getGoodVersion();
        //加上乐观锁
        petProduct.setGoodVersion(version);
        petProductMapper.updateById(petProduct);
        //2.删除缓存
        stringRedisTemplate.delete(CACHE_PET_KEY + petProduct.getGoodId());
        return Result.ok("修改成功");
    }


    /**
     * 根据宠物商品id查询商品信息
     *
     * @param goodId
     */
    @Override
    public Result queryPetProductById(Integer goodId) {
        //互斥锁解决缓存击穿
        PetProduct petProduct = queryWithMutex(goodId);
        if (petProduct == null) {
            return Result.error("商品不存在！");
        }
        //7.返回
        return Result.ok(petProduct);
    }

    /**
     * 用缓存空值解决缓存击穿，实现根据id查询商品功能
     */
    public Result queryPetProductWithPassThrough(Integer goodId) {
        String key = CACHE_PET_KEY + goodId;
        PetProduct petProduct;
        //1.从redis查询宠物商品缓存
        String petProductJson = stringRedisTemplate.opsForValue().get(key);
        //2.判断是否存在
        if (StrUtil.isNotBlank(petProductJson)) {
            //3.存在就直接返回
            petProduct = JSONUtil.toBean(petProductJson, PetProduct.class);
            return Result.ok(petProduct);
        }
        //判断命中的是不是空值
        if (petProductJson != null) {
            return null;
        }
        //4.不存在就根据id查询数据库
        petProduct = getById(goodId);
        //5.库存中没有该商品,返回null
        if (petProduct == null) {
            //把空值写入redis
            stringRedisTemplate.opsForValue().set(key, "", CACHE_PET_TTL, TimeUnit.MINUTES);
            return null;
        }
        //6.库存存在该商品，写入redis，采用超时剔除的缓存更新策略，30分钟后删除
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(petProduct), CACHE_PET_TTL, TimeUnit.MINUTES);
        //7.返回
        return Result.ok(petProduct);
    }

    /**
     * 用互斥锁在缓存空值的基础上解决缓存穿透问题
     */

    public PetProduct queryWithMutex(Integer goodId) {
        String key = CACHE_PET_KEY + goodId;
        PetProduct petProduct;
        //1.从redis查询宠物商品缓存
        String petProductJson = stringRedisTemplate.opsForValue().get(key);
        //2.判断是否存在
        if (StrUtil.isNotBlank(petProductJson)) {
            //3.存在就直接返回
            petProduct = JSONUtil.toBean(petProductJson, PetProduct.class);
            return petProduct;
        }
        //判断命中的是不是空值
        if (petProductJson != null) {
            return null;
        }
        //4.实现缓存重建
        //4.1.获取互斥锁
        String lockKey = CACHE_PET_LOCK_KEY + goodId;
        try {
            boolean isLock = tryLock(lockKey);
            //4.2.判断是否获取成功锁
            if (!isLock) {
                //4.3.失败了就休眠等待重试
                Thread.sleep(30);
                //重试，采用递归方法
                return queryWithMutex(goodId);
            }
            //4.4.成功，再次检查缓存是否存在，因为在多线程下可能被别人更改了缓存，然后再根据id查询数据库
            //再次检查缓存是否为空
            String petProductJsonAgain = stringRedisTemplate.opsForValue().get(key);
            //判断是否存在
            if (StrUtil.isNotBlank(petProductJsonAgain)) {
                //3.存在就直接返回
                return JSONUtil.toBean(petProductJsonAgain, PetProduct.class);
            }
            //缓存中不存在，查询数据库
            petProduct = getById(goodId);
            //5.库存中没有该商品,返回null
            if (petProduct == null) {
                //把空值写入redis
                stringRedisTemplate.opsForValue().set(key, "", CACHE_PET_TTL, TimeUnit.MINUTES);
                return null;
            }
            //6.库存存在该商品，写入redis，采用超时剔除的缓存更新策略，30分钟后删除
            //模拟缓存重建的延迟，在期间产生高并发
            Thread.sleep(300);
            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(petProduct), CACHE_PET_TTL, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            //7.释放互斥锁,无论如何都会释放锁
            unlock(lockKey);
        }
        //8.返回
        return petProduct;
    }

    /**
     * 仅仅查询第一页商品
     *
     * @param goodName
     * @param goodCategory
     * @param pageCount
     * @param productPerPage
     * @return
     */
    @Override
    public Result queryFirstPetProduct(String goodName, String goodCategory, Integer pageCount, Integer productPerPage) {
        Page<PetProduct> page = new Page<>(1, productPerPage);
        Page<PetProduct> productsPage;
        LambdaQueryWrapper<PetProduct> lambdaQueryWrapper
                = new LambdaQueryWrapper<>();
        if (goodCategory.isEmpty() && goodName.isEmpty()) {
            //名字和类别都没传入，则无条件分页查询
            productsPage = petProductMapper.selectPage(page, null);
        } else if (goodCategory.isEmpty()) {
            //仅仅传入名字，根据名字模糊分页查询
            lambdaQueryWrapper
                    .like(PetProduct::getGoodName, goodName);
            productsPage = petProductMapper.selectPage(page, lambdaQueryWrapper);
        } else if (goodName.isEmpty()) {
            //仅仅传入类别，根据类别精确分页查询
            lambdaQueryWrapper
                    .eq(PetProduct::getGoodCategory, goodCategory);
            productsPage = petProductMapper.selectPage(page, lambdaQueryWrapper);
        } else {
            lambdaQueryWrapper.like(PetProduct::getGoodName, goodName)
                    .eq(PetProduct::getGoodCategory, goodCategory);
            productsPage = petProductMapper.selectPage(page, lambdaQueryWrapper);
        }
        return Result.ok(productsPage);
    }

    /**
     * 查询所有宠物商品
     */
    @Override
    public Result queryPetProduct(String goodName, String goodCategory, Integer pageCount, Integer productPerPage) {
        Page<PetProduct> page = new Page<>(pageCount, productPerPage);
        LambdaQueryWrapper<PetProduct> lambdaQueryWrapper
                = new LambdaQueryWrapper<>();
        Page<PetProduct> productsPage;
        if (goodCategory.isEmpty() && goodName.isEmpty()) {
            //名字和类别都没传入，则无条件分页查询
            productsPage = petProductMapper.selectPage(page, null);
            return Result.ok(productsPage);
        }
        if (goodCategory.isEmpty()) {
            //仅仅传入名字，根据名字模糊分页查询，如果输入了性别中的公母二字，也可以模糊查询出性别为公或者母的宠物
            lambdaQueryWrapper
                    .like(PetProduct::getGoodName, goodName)
                    .or()
                    .like(PetProduct::getGoodSex, goodName);
            productsPage = petProductMapper.selectPage(page, lambdaQueryWrapper);
        } else if (goodName.isEmpty()) {
            //仅仅传入类别，根据类别精确分页查询
            lambdaQueryWrapper
                    .eq(PetProduct::getGoodCategory, goodCategory);
            productsPage = petProductMapper.selectPage(page, lambdaQueryWrapper);
        } else {
            lambdaQueryWrapper
                    .and(Wrapper -> Wrapper
                            .like(PetProduct::getGoodName, goodName)
                            .or()
                            .like(PetProduct::getGoodSex, goodName))
                    .eq(PetProduct::getGoodCategory, goodCategory);
            productsPage = petProductMapper.selectPage(page, lambdaQueryWrapper);
        }
        return Result.ok(productsPage);
    }

    //获取锁，互斥锁解决缓存击穿
    private boolean tryLock(String key) {
        //这个方法=redis里的setnx操作，仅仅在该key不存在时才进行赋值，存在的话不赋值
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);
    }

    //释放锁
    private void unlock(String key) {
        stringRedisTemplate.delete(key);
    }


}




